import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;


public class RocketAnalysisTool 
{
	private Project project;	
	
	public RocketAnalysisTool()
	{
		project = new Project();		
	}

	/**
	 * Allows user to create project using console input. 
	 */
	public void createNewProject()
	{
		Date startDate = null;
		String projectName = "", projectDescription = "", projectStartDate = "";
		 				
		// Obtain project name from user
		System.out.println();
		System.out.println("-New Project-----------------------");		
		System.out.println("Enter the Project Name: ");
		projectName = readInput();
		
		// Obtain project start date from user OR use current
		System.out.println("Enter the Start Date DD/MM/YYYY : ");
		projectStartDate = readInput(); //User may or may not enter the start date,it's optional
		startDate = processDate(projectStartDate);		
		
		// Obtain project description (optional)
		System.out.println("Enter the Project Description : ");
		projectDescription = readInput();		
		
		// Set persistent data 
		project.setName(projectName);
		project.setStartDate(startDate);
		project.setDescription(projectDescription);
		
	}
	
	public void addVersion(String[] fileList)
	{
		Date releaseDate = null;
		String versionID = "", versionReleaseDate;
		
		// Obtain Version ID from user
		System.out.println();
		System.out.println("-Add Version-----------------------");		
		System.out.println("Enter unique Version identifier: ");
		versionID = readInput();

		// Obtain Version release date from user
		System.out.println("Enter the Start Date DD/MM/YYYY : ");
		versionReleaseDate = readInput(); 
		releaseDate = processDate(versionReleaseDate);
		
		// TODO: Obtain version file listing from user
		LinkedList<String> sourceList = new LinkedList();
		for (String sourceFile : fileList)
			sourceList.add(sourceFile);
		
		// Set persistent data 
		Version version = new Version(versionID, releaseDate);
		version.setSourceList(sourceList);
		version.setReleaseSequenceNum(project.generateReleaseSequenceNum());		
		// Add new version loaded project
		project.addVersion(version);		
	}
	
	public void analyseVersion()
	{
		System.out.println();
		System.out.println("-Analyse Version--------------------");
		
		Hashtable<String, ClassMetricExtractor> extractors = new Hashtable<String, ClassMetricExtractor>();
		
		// TODO: Remove hard-coded value
		Version version = project.getVersionList().get(0);
		// Loop through each source file and analyse
		for(String sourceFile : version.getSourceList())
		{
			System.out.println("Analysing: " + sourceFile);
			try 
			{ 
				ClassMetricExtractor cme = new ClassMetricExtractor(new FileInputStream(sourceFile));
				extractors.put(cme.className, cme);
				
				MetricClass metricClass = new MetricClass();
				metricClass.setFanOutClasses(cme.dependencies);
				metricClass.setFanOutCount(cme.fanOutCount);
				metricClass.setName(cme.className);
				metricClass.setSuperClassCount(cme.superClassCount);
				metricClass.setSuperClassName(cme.superClassName);
				metricClass.setFieldCount(cme.fieldCount);
				metricClass.setMethodCount(cme.methodCount);
				metricClass.setPublicMethodCount(cme.publicMethodCount);
				metricClass.setPrivateMethodCount(cme.privateMethodCount);
				metricClass.setProtectedMethodCount(cme.protectedMethodCount);
				
				version.addMetricClass(metricClass);							
			}
			catch(IOException e)
			{
				System.out.println(e);
				/*R emove evil catch block */ 
			}			
		}
		
		//Calculate the fan in for each extractor
		calculateFanIn(extractors);
		
		// Update fan in.
		Enumeration e = extractors.elements();
		while (e.hasMoreElements())
		{
			ClassMetricExtractor cme = (ClassMetricExtractor)e.nextElement();
			MetricClass metricClass = version.getMetricClass(cme.className);
			metricClass.setFanInClasses(cme.getFanIn());
			metricClass.setFanInCount(cme.fanInCount);
		}
	}
	
	public void saveProject()
	{		
		System.out.println();
		System.out.println("-Save Project-----------------------");
		
		try
		{		
			// Uses project name as fileName
			String fileName = project.getName().concat(".xml"); 
			// Serialize project to XML string
			XStream xStream = new XStream(new DomDriver());			
			String xmlData = xStream.toXML(project);
			// Write XML to file
			PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));
			pw.write(xmlData);
			pw.close();
			
			System.out.println("Project saved to: " + fileName);
		} 
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	
	public void saveProject(String fileName)
	{
		System.out.println();
		System.out.println("-Save Project-----------------------");
		
		try
		{					
			// Serialize project to XML string
			XStream xStream = new XStream(new DomDriver());			
			String xmlData = xStream.toXML(project);
			// Write XML to file
			PrintWriter pw = new PrintWriter(new FileOutputStream(fileName));
			pw.write(xmlData);
			pw.close();
			
			System.out.println("Project saved to: " + fileName);
		} 
		catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * Method to read input from the keyboard
	 */
	private String readInput()
	{
		BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));
		String strInput= "";
		
		try
		{
			strInput = keyboard.readLine();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		return strInput;
	}
	
	
	/**
	 * Displays the fanIn data for the specified ClassMetricExtractor
	 * @param cme - the ClassMetricExtractor.
	 */
	private void displayFanIn(ClassMetricExtractor cme)
	{
		System.out.println("Printing FanIn for class: " + cme.className);
		Hashtable<String, String> table = cme.getFanIn();
		Enumeration e = table.elements();
		
		while (e.hasMoreElements())
		{
			System.out.println(e.nextElement());
		}
	}

	private void calculateFanIn(Hashtable<String, ClassMetricExtractor> extractors)
	{
		Enumeration e = extractors.elements();
		while (e.hasMoreElements())
		{
			ClassMetricExtractor cme = (ClassMetricExtractor)e.nextElement();
			
			LinkedList<String> fanOutNodes = new LinkedList<String>(cme.dependencies);
						
			for(String fanOutNode : fanOutNodes)
			{
				try
				{
					ClassMetricExtractor cmeIn = extractors.get(fanOutNode);				
					cmeIn.addFanIn(cme.className);
				}
				catch(NullPointerException err) {}
			}			
		}		
	}
	
	/**
	 * @param strDate
	 * @param dtStartDate
	 * @return
	 */
	private Date processDate(String strDate)
	{
		Date dtStartDate = null;
		
		if(strDate.length()!=0)//checks if the date is entered or not
		{
			int year,month,day;
			try
			{
				day=Integer.parseInt(strDate.substring(0,2));
				month=Integer.parseInt(strDate.substring(3,5))-1;
				year=Integer.parseInt(strDate.substring(6,10));
				
				if(day>31||month>12)
				{
					System.err.println("Invalid date entered...current date will be added");
					dtStartDate=new Date();
				}
				else
				{
					Calendar calendar=Calendar.getInstance();
					calendar.set(year,month,day);

					Date currentDate=new Date();
					
					//Check if the date entered is a past date or not.
					if(currentDate.before(calendar.getTime()))
					{
						System.err.println("Start date must be either Today or a date in the past...current date will be added");
						dtStartDate=currentDate;
					}
					else
					{
						dtStartDate=calendar.getTime();
					}
				}
			}
			catch(StringIndexOutOfBoundsException s)
			{
				dtStartDate=new Date();
				System.err.println("Invalid start date entered...current date will be added...");
			}
			catch(NumberFormatException n)
			{
				dtStartDate=new Date();
				System.err.println("Invalid start date entered...current date will be added...");
			}
		}
		else { dtStartDate = new Date(); }
		
		return dtStartDate;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{						
		displayHeader(); // Displays the output header
		
		RocketAnalysisTool rat = new RocketAnalysisTool();								   
		rat.createNewProject();		
		rat.addVersion(args);
		rat.analyseVersion();
		rat.saveProject(); // Save using default fileName (the project name)
		
		System.out.println("-----------------------------------");		
	}

	/**
	 * 
	 */
	private static void displayHeader()
	{
		System.out.println("Rocket Analysis Tool - Iteration 0");				
		System.out.println("-----------------------------------");		
	}		
}
